using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using CodeContrib.Web.Mvc.Html;

namespace CodeContrib.Web.Mvc.ComponentModel.DataAnnotations
{
	[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
	public sealed class DropDownListAttribute : Attribute, ITemplateField
	{
		private static string defaultTemplateName;

		public string DataTextField { get; private set; }
		public string DataValueField { get; private set; }

		public static string DefaultTemplateName
		{
			get
			{
				if (string.IsNullOrEmpty(defaultTemplateName))
				{
					defaultTemplateName = "DropDownList";
				}

				return defaultTemplateName;
			}
			set { defaultTemplateName = value; }
		}

		public IDictionary<string, object> HtmlAttributes { get; private set; }
		public string OptionLabel { get; private set; }
		public string TemplateName { get; private set; }
		public string ViewDataKey { get; private set; }

		public DropDownListAttribute(string viewDataKey, string dataValueField) : this(viewDataKey, dataValueField, null)
		{
		}

		public DropDownListAttribute(string viewDataKey, string dataValueField, string dataTextField) : this(viewDataKey, dataValueField, dataTextField, null)
		{
		}

		public DropDownListAttribute(string viewDataKey, string dataValueField, string dataTextField, string optionLabel) : this(DefaultTemplateName, viewDataKey, dataValueField, dataTextField, optionLabel, null)
		{
		}

		public DropDownListAttribute(string viewDataKey, string dataValueField, string dataTextField, string optionLabel, object htmlAttributes) : this(DefaultTemplateName, viewDataKey, dataValueField, dataTextField, optionLabel, htmlAttributes)
		{
		}

		public DropDownListAttribute(string templateName, string viewDataKey, string dataValueField, string dataTextField, string optionLabel, object htmlAttributes)
		{
			Guard.NotNullOrEmpty(templateName, "templateName");
			Guard.NotNullOrEmpty(viewDataKey, "viewDataKey");
			Guard.NotNullOrEmpty(dataValueField, "dataValueField");

			DataTextField = dataTextField;
			DataValueField = dataValueField;
			HtmlAttributes = htmlAttributes.ToHtmlAttributes();
			OptionLabel = optionLabel;
			TemplateName = templateName;
			ViewDataKey = viewDataKey;
		}

		private static object GetPropertyValue(object model, string propertyName)
		{
			if (model != null)
			{
				var property = GetTypeDescriptor(model.GetType()).GetProperties().Cast<PropertyDescriptor>().SingleOrDefault(x => string.Compare(x.Name, propertyName, StringComparison.OrdinalIgnoreCase) == 0);

				if (property != null)
				{
					return property.GetValue(model);
				}
			}

			return null;
		}

		public object GetSelectedText(object model)
		{
			return GetPropertyValue(model, DataTextField.AsNullIfEmpty() ?? DataValueField);
		}

		public object GetSelectedValue(object model)
		{
			return GetPropertyValue(model, DataValueField);
		}

		private static ICustomTypeDescriptor GetTypeDescriptor(Type type)
		{
			return new AssociatedMetadataTypeTypeDescriptionProvider(type).GetTypeDescriptor(type);
		}
	}
}